/*
 * rpm.c - Generic RPM support
 *
 * Copyright (C) 2015 Intel Corporation
 *
 * cve-check-tool is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */
#define _GNU_SOURCE
#include <glib.h>
#include <libgen.h>
#include <libxml/xmlreader.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "cve-check-tool.h"
#include "plugin.h"
#include "rpm.h"
#include "rpm_common.c"
#include "util.h"

static bool srpm_patch_check(struct source_package_t *t, char *id, bool ignore)
{
        if (!t->extra) {
                return false;
        }

        autofree(gchar) *pnamet = g_ascii_strdown((gchar *)id, -1);
        autofree(gchar) *pname = g_strdup_printf(ignore ? "%s.nopatch" : "%s.patch", pnamet);
        gchar **list = t->extra;

        for (uint i = 0; i < g_strv_length(list); i++) {
                autofree(gchar) *comp = g_strchomp(g_ascii_strdown((gchar *)list[i], -1));
                if (g_str_equal(comp, "")) {
                        continue;
                }
                if (g_str_equal(pname, comp)) {
                        return true;
                }
        }

        return false;
}

void srpm_extra_free(struct source_package_t *pkg)
{
        if (pkg && pkg->extra) {
                g_strfreev(pkg->extra);
                pkg->extra = NULL;
        }
}

struct source_package_t *srpm_examine(const char *filename)
{
        struct source_package_t *t = NULL;
        autofree(gchar) *cmdline = NULL;
        autofree(GError) *error = NULL;
        autofree(gstrv) *splits = NULL;
        int exit = 0;
        autofree(gchar) *output = NULL;
        int len = 0;

        t = calloc(1, sizeof(struct source_package_t));
        if (!t) {
                return NULL;
        }

        if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
                fprintf(stderr, "Required source rpm not present: %s\n", filename);
                free(t);
                return NULL;
        }
        cmdline = g_strdup_printf("rpm -qp --queryformat \'%%{NAME}\t%%{VERSION}\t[%%{PATCH}\t]\n\' %s", filename);
        if (!cmdline) {
                free(t);
                return NULL;
        }
        if (!g_spawn_command_line_sync(cmdline, &output, NULL, &exit, &error)) {
                g_printerr("Unable to run command: %s\n", error->message);
                free(t);
                return NULL;
        }
        if (exit != 0) {
                fprintf(stderr, "Abnormal exit code for package %s: %s\n", filename, output);
        }
        splits = g_strsplit(output, "\t", -1);
        if ((len = g_strv_length(splits)) < 2) {
                fprintf(stderr, "Invalid output from rpm\n");
                return NULL;
        }

        t->name = g_strdup(splits[0]);
        t->version = g_strdup(splits[1]);
        t->type = PACKAGE_TYPE_SRPM;
        if (len > 2) {
                /* TODO: De-glibificate */
                GPtrArray *arr = g_ptr_array_new_with_free_func(NULL);
                for (int i = 0; i < len - 2; i++) {
                        g_ptr_array_add(arr, g_strdup(splits[i + 2]));
                }
                g_ptr_array_add(arr, NULL);
                t->extra = (gchar **)arr->pdata;
                g_ptr_array_free(arr, FALSE);
        }

        return t;
}

struct source_package_t *srpm_scan_archive(const char *dir, const char *name, const char *version, const char *release)
{
        autofree(gchar) *path = NULL;

        path = g_strdup_printf("%s%s%s-%s-%s.src.rpm", dir, G_DIR_SEPARATOR_S, name, version, release);

        if (!path) {
                return NULL;
        }

        return srpm_examine(path);
}

bool srpm_is_patched(struct source_package_t *t, char *id)
{
        return srpm_patch_check(t, id, false);
}

bool srpm_is_ignored(struct source_package_t *t, char *id)
{
        return srpm_patch_check(t, id, true);
}

bool srpm_is_package(const char *filename)
{
        return g_str_has_suffix((const gchar *)filename, ".src.rpm");
}

_module_export_ bool cve_plugin_module_init(CvePlugin *self)
{
        self->flags = PLUGIN_TYPE_PACKAGE;
        self->name = "srpm";
        self->is_ignored = srpm_is_ignored;
        self->is_patched = srpm_is_patched;
        self->is_package = srpm_is_package;
        self->scan_archive = srpm_scan_archive;
        self->scan_package = srpm_examine;
        self->free_package = srpm_extra_free;
        return true;
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 expandtab:
 * :indentSize=8:tabSize=8:noTabs=true:
 */
